#第十六章 协程

#创建一个最简单的协程

import inspect


def simple_coroutine():
    print("->coroutine started ")
    x = yield # 右边没有表达式 因为只是接收数据 将会返回None
    print("->coroutine received ",x)

my_coro = simple_coroutine() #创建协程 就是一个生成器
state = inspect.getgeneratorstate(my_coro)
print("state=",state)
next(my_coro) #启动协程
#my_coro.send(200)

def simple_coro2(a):
    print('-> Started: a =', a)
    b = yield a #yield右边的值作为结果返回给send函数
    print('-> Received: b =', b)
    c = yield a + b
    print('-> Received: c =', c)

my_coro2 = simple_coro2(10)
next(my_coro2)

try:    
    ret = my_coro2.send(20)
    print("my_coro2 ---",ret)  
except StopIteration:    
    print("end coro....")
print("my_coro2 === ",ret)

#协程返回值 并获取其返回值

from collections import namedtuple

Result = namedtuple("Result",'count avg')

#子生成器
def avgrager():
    '''
    子生成器
    '''
    total = 0.0
    count = 0
    avg = 0    
    print("avgrager started ...XXXX....")
    while True:        
        term = yield avg #yield右边的表达式结果会返回给调用send方法的协程 左边的是接收来自send方法发送的数据
        if term is None:
            break
        print('term:',term,"count",count)
        total += term
        count += 1
        avg = total/count
        print('avg=',avg)
    return Result(count,avg)


avg_coro = avgrager()
next(avg_coro)
avg_coro.send(10)
avg_coro.send(50)


try:
    avg_coro.send(None)
except StopIteration as  exc:
    print('exc.value',exc.value) #捕获协程函数返回的值 该值会附加在异常的value属性上


#说明yield from
#委派生成器
def grouper(results,key): #该函数是通过yield from 返回生成器 yield from 关键字是通过子生成器avgrager生成器函数返回
    while True:
        #执行到yield from 会阻塞（暂停）并等待avgrager执行
        results[key] = yield from avgrager() # yield from 关键字处理avgrager函数 该函数返回的值会绑定到 results[key] 

#调用方 客户端代码
def main(data):
    results = {}
    for key,values in data.items():
        group = grouper(results,key) #该函数通过一个 yield from 关键字调用 avgrager()(子生成器) 返回一个协程(生成器) 
        print("group object:",group)
        next(group) #启动协程
        for value in values:
            group.send(value) #发送数据给子生成器
        group.send(None) #结束协程,子生成器 avgrager 函数会返回结果
    print("results=",results)
    report(results)

def report(results):
    for key,result  in sorted(results.items()):
        group,unit = key.split(";")
        print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.avg, unit))

data = {
'girls;kg':
[40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
'girls;m':
[1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
'boys;kg':
[39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
'boys;m':
[1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}

main(data)                                        

def myCount():
    count = 0
    while True:
        val = yield count #返回给调用方
        if val is None:
            break
        count += val
        print('count::',count)
count_coro = myCount()

next(count_coro)
print(count_coro.send(10))

print("count_coro:",count_coro)

def myCount2():
    while True:
        x = yield from myCount()

my = myCount2()
print('my.send=',my.send(None))
print('my===',my.send(10))

'''
我对yield from 的用法做了两点陈述，摘要如下。
• 使用yield from 链接的多个协程最终必须由不是协程的调用方驱动(重点)，调用方显式或隐
式（例如，在for 循环中）在最外层委派生成器上调用next(...) 函数或.send(...) 方法。

    例如：上面的例子 就是要通过 grouper 函数调用 而不能由main函数调用，这是固定用法没有为什么

• 链条中最内层的子生成器必须是简单的生成器（只使用yield）或可迭代的对象。

'''